/* Universidad del Valle de Guatemala
 * CC2003 - Algoritmos y Estructuras de Datos
 * PROYECTO #3: PI-LISP
 * Fecha de Entrega: Lunes 21 de Noviembre de 2011
 */

/**
 * Clase que representa los átomos del intérprete Pi-Lisp.
 * @authors José Antonio Bagur - 10275, Emilio José Miranda - 10411, Luis Antonio Siguenza - 10297
 */

public class Atom {
    
            /* VARIABLES DE INSTANCIA */
            
            protected Lista List1;
            protected String des;
            protected String atom1;
            protected double number;        
            protected boolean isList;
            protected boolean isNull;
            protected double numberVal;
            protected boolean isNumber;
            protected boolean isBoolean;
            protected boolean booleanVal;
            
            /**
             * Constructor de la clase Atom, inicializa el estado del Átomo.
             */
            public Atom() {
                this.isNull = true;
                this.isNumber = this.isList = false;
            }
            
            /**
             * Constructor de la clase Atom, inicializa el estado de un Átomo de 
             * tipo List1.
             * @param L 
             */
            public Atom(Lista L) {
                this.List1 = L;
                this.isList = true;
                this.isNull = false;
                this.isNumber = false;
            }
            
            /**
             * Constructor de la clase Atom, inicializa el estado de un Átomo de 
             * tipo Int.
             * @param num 
             */
            public Atom(int num) {
                this.number = num;
                this.isNumber = true;
                this.isList = this.isNull = false;
                this.atom1 = Integer.toString(num);
            }
            
            /**
             * Constructor de la clase Atom, inicializa el estado de un Átomo de
             * tipo Float.
             * @param num 
             */
            public Atom(Double num) {
                this.number = num;
                this.isNumber = true;
                this.isNull = this.isList = false;
                this.atom1 = Double.toString(this.number);
            }
            
            /**
             * Constructor de la clase Atom, inicializa el estado de un Átomo de 
             * tipo Boolean.
             * @param B 
             */
            public Atom(boolean B) {
                if(B) {
                    this.isBoolean = this.booleanVal = true;
                    this.isNumber = this.isNull = this.isList = false;
                    this.atom1 = "T";
                } else {
                    this.isNull = true;
                    this.isList = this.isNumber = false;
                }
            }
            
            /**
             * Constructor de la clase Atom, inicializa el estado de un Átomo 
             * personalizado.
             * @param A
             * @param des 
             */
            public Atom(String A, String exp) {
                this.atom1 = A;
                this.des = exp;
            }
            
            /**
             * Constructor de la clase Atom, inicializa el estado de un Átomo de 
             * tipo String.
             * @param A 
             */
            public Atom(String A) {
                try {
                    Atom dummyAtom = new Atom(Integer.parseInt(A)); 
                    this.copyThis(dummyAtom);
                    this.isNumber = true;
                } catch(Exception E) {
                    try {
                        Atom dummyAtom2 = new Atom(Double.parseDouble(A));
                        this.copyThis(dummyAtom2);
                        this.isNumber = true;
                    } catch(Exception EX) {
                        this.atom1 = A;
                        this.isNumber = this.isList = this.isNull = false;
                    }
                }                
            }
            
            /**
             * Método que permite hacer una copia del átomo.
             * @param A átomo a copiar 
             */
            public void copyThis(Atom A) {
                this.des = A.des;
                this.atom1 = A.atom1;
                this.List1 = A.List1;
                this.number = A.number;
                this.isList = A.isList;
                this.isNull = A.isNull;
                this.isNumber = A.isNumber;
            }
            
            /**
             * Método que permite verificar si un átomo es de tipo entero.
             * @return True si el átomo es de tipo entero
             */
            public boolean isInt() {
                if(!this.isNumber) {
                    return false;
                }
                
                try {
                    int dummyNum = Integer.parseInt(this.atom1);
                    return true;
                } catch (Exception E) {
                    return false;
                }
            }
            
            /**
             * Método que permite verificar si la lista es de operación.
             * @return True si la lista posee una operación
             */
            public boolean isOpList() {
                if(this.isList) {
                    return (this.List1.isOp);
                } else {
                    return false;   
                }
            }
            
            /**
             * Método que permite verificar si un átomo de tipo String inicia con un 
             * substring determinado
             * @param des
             * @return True si inicia con des
             */
            public boolean startsWith(String exp) {
                if(exp.length() <= this.atom1.length() && !this.isList) {
                        return this.atom1.substring(0, exp.length()).equals(exp);
                }
                return false;
            }
            
            /** 
             * Método que permite conocer si un átomo de tipo String termina con un
             * substring determinado
             * @param des buscada al final del átomo
             * @return True si termina con des
             */
            public boolean endsWith(String exp) {
                if(!this.isList) {
                    return this.atom1.lastIndexOf(exp) == this.atom1.length() - exp.length();
                }
                
                return false;
            }
            
            /**
             * Método que permite obtener el valor numérico de un átomo determinado.
             * @return número del átomo
             */
            public double getNumber() {
                if(!this.isNumber) {
                    System.out.println("---> ADVERTENCIA: EL VALOR NO ES UN NÚMERO");
                }
                
                return this.number;
            }
            
            public boolean isNumber() {
                return this.isNumber;
            }
            
            /**
             * Método que permite conocer el tipo del átomo.
             * @return CONS si es una lista, FLOAT si es un número, NULL si está vació
             * y SYMBOL si es una operacion
             */
            public String getType() {
                if(this.isList) {
                    return "CONS";
                } else if(this.isNumber) {
                    return "FLOAT";
                } else if(this.isNull) {
                    return "NULL";
                } else {
                    return "SYMBOL";
                }
            }
            
            /**
             * Método que permite realizar una comparación entre átomos.
             * @param O átomo a comparar
             * @return True si los átomos son iguales
             */
            public boolean equals(Object O) {
                Atom dummyAtom = (Atom) O;
                
                if(this.isNull) {
                    if(dummyAtom.isNull) {
                        return true;
                    }
                }
                
                if(this.isNumber) {
                    if(dummyAtom.isNumber) {
                        if(this.number == dummyAtom.number) {
                            return true;
                        }
                    }
                }
                
                if(this.isList) {
                    if(dummyAtom.isList) {
                        if(this.List1.equals(dummyAtom.List1)) {
                            return true;
                        }
                    }
                }
                
                return this.atom1.equals(dummyAtom.atom1);  
            }
            
            /**
             * Método toString de la clase Atom.
             * @return representación en String del átomo
             */
            public String toString() {
                if(this.isNull) {
                    return "NIL";
                } else if(this.isList) {
                    return this.List1.toString();
                } else {
                    return this.atom1;
                }
            }
    
}   // FIN DE LA CLASE ATOM